package view;

import java.awt.Color;
import java.awt.Frame;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;

import javax.swing.AbstractAction;
import javax.swing.BorderFactory;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JSeparator;
import javax.swing.JTextField;
import javax.swing.KeyStroke;
import javax.swing.border.BevelBorder;
import javax.swing.border.LineBorder;
import javax.swing.filechooser.FileFilter;

import org.bushe.swing.event.EventBus;
import org.bushe.swing.event.annotation.*;

import constants.Constants;
//What for?
import constants.Events;
import control.Main;
import model.i.IModel;


/**
 * This code was edited or generated using CloudGarden's Jigloo
 * SWT/Swing GUI Builder, which is free for non-commercial
 * use. If Jigloo is being used commercially (ie, by a corporation,
 * company or business for any purpose whatever) then you
 * should purchase a license for each developer using Jigloo.
 * Please visit www.cloudgarden.com for details.
 * Use of Jigloo implies acceptance of these licensing terms.
 * A COMMERCIAL LICENSE HAS NOT BEEN PURCHASED FOR
 * THIS MACHINE, SO JIGLOO OR THIS CODE CANNOT BE USED
 * LEGALLY FOR ANY CORPORATE OR COMMERCIAL PURPOSE.
 */
public class View extends JFrame {
    private static final long serialVersionUID = 1L;

    //Menu-related variables
    private JMenuBar menuBar;
    private JMenu menu;
    private JMenu menuSound;
    private JMenu menuHelp;
    private JMenu menuSettings;
    private JMenuItem menuNewGame;
    private JMenuItem menuNetwork;
    private JMenuItem menuSave;
    private JMenuItem menuLoad;
    private AbstractAction rulesButtonAction;
    private JMenuItem rulesButton;
    private JLabel labelTime;
    private AbstractAction highScoreButtonAction;
    private AbstractAction actionThrow;
    private AbstractAction actionSave;
    private AbstractAction actionLoad;
    private AbstractAction actionMusic;
    private JMenuItem buttonViewHighScore;
    private JMenu menuHighScore;
    private JMenuItem exitButton;
    private JMenuItem menuItemShowHelp;
    private JMenuItem menuItemAbout;
    private JCheckBoxMenuItem menuMusic;
    private JCheckBoxMenuItem menuFX;
    private JSeparator menuSeparator1;
    private JSeparator menuSeparator2;
    private AbstractAction exitAction;
    private AbstractAction menuAboutAction;
    private AbstractAction menuHelpAction;
    private AbstractAction abstractNewGame;
    private AbstractAction playerButtonAction;
    private JMenuItem playerButton;
    private Help h = new Help();

    //Important gameplay related variables
    private JTextField txtDie;  //Show the result of a die throw
    private JTextField txtTurn; //Who's turn is it?
    private JTextField txtTime; //Play your turn before the time runs out.

    //Other variables
    private DieButton btnDie;
    private JLabel lblDie;
    private JLabel lblTurn;
    private JLabel lblP4;
    private JLabel lblP3;
    private JLabel lblP2;
    private JLabel lblP1;
    private StartBox panelP3;
    private StartBox panelP2;
    private StartBox panelP4;
    private StartBox panelP1;

    private PlayerSettings pSettings;

    /**
     * A set of arrays to hold pointers to View objects. Used
     * for iteration during event handling.
     */
    private JLabel[] playerNames;
    private StartBox[] startBoxes;

    public JLabel[] getPlayerNames() {
        return playerNames;

    }

    public View() {
        AnnotationProcessor.process(this);
        initGUI();
        initSquares();

        StartBox[] startBoxes2 = {panelP1, panelP2, panelP3, panelP4};

        this.startBoxes = startBoxes2;
        pSettings = new PlayerSettings(this);
        playerButton.setEnabled(false);
        this.setIconImage(getToolkit().getImage(Constants.GUI.main_window_icon));
        repaint();
    }

    /**
     * Initiate the View, which is the main graphical form for Ludo3k.
     */
    private void initGUI() {
        try {
            {
                getContentPane().setLayout(null);
                getContentPane().setBackground(new java.awt.Color(255, 255, 255));
                this.setTitle(Constants.GUI.main_window_title);
                this.setResizable(false);
                this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                {
                    menuBar = new JMenuBar();
                    setJMenuBar(menuBar);
                    {
                        menu = new JMenu();
                        menuBar.add(menu);
                        menuBar.add(getMenuSettings());
                        menuBar.add(getMenuHelp());
                        menuBar.add(getMenuHighScore());
                        menu.setText("File");
                        {
                            exitButton = new JMenuItem();
                            menu.add(getMenuNewGame());
                            menu.add(getMenuNetwork());
                            menu.add(getMenuSeparator2());
                            menu.add(getMenuSave());
                            menu.add(getMenuLoad());
                            menu.add(getMenuSeparator1());
                            menu.add(exitButton);
                            exitButton.setAction(getExitAction());
                            exitButton.setText("Exit");
                            exitButton.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Q, ActionEvent.ALT_MASK));
                        }
                    }
                }
            }
            pack();
            this.setSize(Constants.GUI.main_window_width, Constants.GUI.main_window_height);
            setLocationRelativeTo(null);
            this.setFont(new java.awt.Font("Tahoma", 0, 10));
            getContentPane().add(getBtnDie());
            getContentPane().add(getTxtTime());
            getContentPane().add(getLblDie());
            getContentPane().add(getTxtDie());
            getContentPane().add(getLblTurn());
            getContentPane().add(getTxtTurn());
            getContentPane().add(getPanelP1());
            getContentPane().add(getPanelP2());
            getContentPane().add(getPanelP3());
            getContentPane().add(getPanelP4());
            getContentPane().add(getLblP1());
            getContentPane().add(getLblP2());
            getContentPane().add(getLblP3());
            getContentPane().add(getLblP4());

        } catch(Exception e) {
            e.printStackTrace();
        }
        JLabel[] playerNames2  = {lblP1, lblP2, lblP3, lblP4};
        this.playerNames = playerNames2;
        repaint();
    }

    /**
     * If the menu item "Settings" is not initiated, initiate it,
     * and return a reference.
     * @return menuSettings
     */
    private JMenu getMenuSettings() {
        if(menuSettings == null) {
            menuSettings = new JMenu();
            menuSettings.setText("Settings");
            menuSettings.add(getMenuSound());
            menuSettings.add(getRulesButton());
            menuSettings.add(getPlayerButton());
        }
        return menuSettings;
    }

    /**
     * If the menu item "Help" is not initiated, initiate it,
     * and return a reference. Add two items to it: "About"
     * and "Show Help".
     * @return menuHelp
     */
    private JMenu getMenuHelp() {
        if(menuHelp == null) {
            menuHelp = new JMenu();
            menuHelp.setText("Help");
            menuHelp.add(getMenuItemAbout());
            menuHelp.add(getMenuItemShowHelp());
        }
        return menuHelp;
    }


    /**
     * If the menu sub-item "Show Help" is not initiated, initiate it,
     * and return a reference.
     * @return menuItemShowHelp
     */
    private JMenuItem getMenuItemShowHelp() {
        if(menuItemShowHelp == null) {
            menuItemShowHelp = new JMenuItem();
            menuItemShowHelp.setText("Show Help");
            menuItemShowHelp.setAction(getMenuHelpAction());
        }
        return menuItemShowHelp;
    }

    /**
     * If the menu sub-item "Save" is not initiated, initiate it,
     * and return a reference.
     * @return menuSave
     */
    private JMenuItem getMenuSave() {
        if(menuSave == null) {
            menuSave = new JMenuItem();
            menuSave.setText("Save");
            menuSave.setAction(getActionSave());
            menuSave.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, ActionEvent.ALT_MASK));
        }
        return menuSave;
    }

    /**
     * If the menu sub-item "Sound" is not initiated, initiate it,
     * and return a reference. Also add two further sub-items to
     * it; menuFX and menuMusic.
     * @return menuSound
     */
    private JMenu getMenuSound() {
        if(menuSound == null) {
            menuSound = new JMenu();
            menuSound.setText("Sound");
            menuSound.add(getMenuFX());
            menuSound.add(getMenuMusic());
        }
        return menuSound;
    }

    /**
     * If the menu sub-item "menuFX" is not initiated, initiate it,
     * and return a reference.
     * @return menuFX
     */
    private JCheckBoxMenuItem getMenuFX() {
        if(menuFX == null) {
            menuFX = new JCheckBoxMenuItem("FX", false);
            menuFX.setText("FX");
            menuFX.setAction(getAbstractActionFX());
            menuFX.setSelected(Main.s.isSFXEnabled());
        }
        return menuFX;
    }

    /**
     * If the menu sub-item "menuMusic" is not initiated, initiate it,
     * and return a reference.
     * @return
     */
    private JCheckBoxMenuItem getMenuMusic() {
        if(menuMusic == null) {
            menuMusic = new JCheckBoxMenuItem("Music", false);
            menuMusic.setText("Music");
        }
        return menuMusic;
    }


    /**
     * If the menu sub-item "fxMusic" is not initiated, initiate it,
     * and return a reference.
     * @return
     */
    @SuppressWarnings("serial")
    private AbstractAction getAbstractActionFX() {
        if(actionMusic == null) {
            actionMusic = new AbstractAction("FX", null) {

                public void actionPerformed(ActionEvent evt) {
                    //Publish SFX true/false (toggle on/off)
                    EventBus.publish(Events.SFX.toString(), menuFX.isSelected());
                }
            };
        }
        return actionMusic;
    }

    /**
     * If the menu sub-item "Load" is not initiated, initiate it,
     * and return a reference.
     * @return menuLoad
     */
    private JMenuItem getMenuLoad() {
        if(menuLoad == null) {
            menuLoad = new JMenuItem();
            menuLoad.setText("Load");
            menuLoad.setAction(getActionLoad());
            menuLoad.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_L, ActionEvent.ALT_MASK));
        }
        return menuLoad;
    }

    /**
     * If the menu sub-item "New Game" is not initiated, initiate it,
     * and return a reference.
     * @return menuNewGame
     */
    private JMenuItem getMenuNewGame() {
        if(menuNewGame == null) {
            menuNewGame = new JMenuItem();
            menuNewGame.setText("New Game");
            menuNewGame.setAction(getAbstractNewGame());
            menuNewGame.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_N, ActionEvent.ALT_MASK));
        }
        return menuNewGame;
    }

    /**
     * If the menu sub-item "Network" is not initiated, initiate it,
     * and return a reference.
     * @return
     */
    private JMenuItem getMenuNetwork() {
        if(menuNetwork == null) {
            menuNetwork = new JMenuItem();
            menuNetwork.setText("Network Game");
        }
        return menuNetwork;
    }

    /**
     * If the menu sub-item "About" is not initiated, initiate it,
     * and return a reference.
     * @return
     */
    private JMenuItem getMenuItemAbout() {
        if(menuItemAbout == null) {
            menuItemAbout = new JMenuItem();
            menuItemAbout.setText("jMenuItem1");
            menuItemAbout.setAction(getMenuAboutAction());
            menuItemAbout.setBounds(51, 19, 56, 19);
        }
        return menuItemAbout;
    }


    /**
     * Associate an action with clicking the "About" option on the
     * About tab.
     * @return menuHelpAction
     */
    private AbstractAction getMenuAboutAction() {
        if(menuAboutAction == null) {
            final View v = this;
            menuAboutAction = new AbstractAction("About Us", null) {
                private static final long serialVersionUID = 1L;
                public void actionPerformed(ActionEvent evt) {
                    new About(v);
                }
            };
        }
        return menuAboutAction;
    }

    /**
     * Associate an action with clicking the "Show Help" option on the
     * About tab.
     * @return menuHelpAction
     */
    private AbstractAction getMenuHelpAction() {
        if(menuHelpAction == null) {
            menuHelpAction = new AbstractAction("Show Help", null) {
                private static final long serialVersionUID = 1L;
                public void actionPerformed(ActionEvent evt) {
                    h.setVisible(true);
                }
            };
        }
        return menuHelpAction;
    }

    /**
     * Associate an action with exiting the application
     * @return exitAction
     */
    private AbstractAction getExitAction() {
        if(exitAction == null) {
            exitAction = new AbstractAction("Exit", null) {
                private static final long serialVersionUID = 1L;
                public void actionPerformed(ActionEvent evt) {
                    EventBus.publish(Events.END_GAME.toString(), null);
                    disposeAll();
                }
            };
        }
        return exitAction;
    }

    /**
     * Dispose of all Frames created by the application.
     * This will terminate the application!
     */
    private void disposeAll() {
        Frame[] allFrames = JFrame.getFrames();
        for(Frame f: allFrames)
            f.dispose();
        System.out.println("Final");
        EventBus.clearAllSubscribers();
    }

    /**
     * Initiate the first menuSeparator if it is not initiated already
     * @return menuSeparator1
     */
    private JSeparator getMenuSeparator1() {
        if(menuSeparator1 == null) {
            menuSeparator1 = new JSeparator();
        }
        return menuSeparator1;
    }

    /**
     * Initiate the second menuSeparator if it is not initialized already.
     * @return menuSeparator2
     */
    private JSeparator getMenuSeparator2() {
        if(menuSeparator2 == null) {
            menuSeparator2 = new JSeparator();
        }
        return menuSeparator2;
    }

    /**
     * The button that throws the die.
     * @return btnDie
     */
    private JButton getBtnDie() {
        if(btnDie == null) {
            btnDie = new DieButton();
            btnDie.setText("Die");
            btnDie.setBounds(352, 270, 51, 51);
            btnDie.setAction(getActionThrow());
            btnDie.setToolTipText("Throw the Die!");
            btnDie.setMargin(new Insets(2, 2, 2, 2));
            btnDie.setIcon(new ImageIcon("img/die6.png"));
            btnDie.setFont(new java.awt.Font("Tahoma", 0, 11));
            btnDie.setPreferredSize(new java.awt.Dimension(65, 60));
        }
        return btnDie;
    }



    private AbstractAction getActionThrow() {
        if(actionThrow == null) {
            actionThrow = new AbstractAction("THROW", null) {
                private static final long serialVersionUID = 1L;

                public void actionPerformed(ActionEvent evt) {
                    System.out.println(evt.getActionCommand());
                    System.out.println(evt.getID());
                    System.out.println(evt.getModifiers());
                    System.out.println(evt.getSource());
                    System.out.println(evt.getWhen());

                    EventBus.publish(Events.DICETHROW.toString(), null);
                }
            };
        }
        return actionThrow;
    }

    private JMenu getMenuHighScore() {
        if(menuHighScore == null) {
            menuHighScore = new JMenu();
            menuHighScore.setText("High Score");
            menuHighScore.add(getButtonViewHighScore());
        }
        return menuHighScore;
    }

    private JMenuItem getButtonViewHighScore() {
        if(buttonViewHighScore == null) {
            buttonViewHighScore = new JMenuItem();
            buttonViewHighScore.setText("View High Score");
            buttonViewHighScore.setAction(getHighScoreButtonAction());
        }
        return buttonViewHighScore;
    }

    HighScoreView hsv = null;
    @SuppressWarnings("serial")
    private AbstractAction getHighScoreButtonAction() {
        if(highScoreButtonAction == null) {
            highScoreButtonAction = new AbstractAction("View High Score", null) {
                public void actionPerformed(ActionEvent evt) {
                    if(hsv == null)
                        hsv = new HighScoreView();
                    else
                        hsv.setVisible(true);
                }
            };
        }
        return highScoreButtonAction;
    }

    private JLabel getLabelTime() {
        if(labelTime == null) {
            labelTime = new JLabel();
            labelTime.setText("Time Left:");
            labelTime.setBounds(190, 11, 53, 16);
            labelTime.setFont(new java.awt.Font("Tahoma", 0, 10));
        }
        return labelTime;
    }

    private JMenuItem getRulesButton() {
        if(rulesButton == null) {
            rulesButton = new JMenuItem();
            rulesButton.setText("Rules");
            rulesButton.setAction(getRulesButtonAction());
            rulesButton.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_R, ActionEvent.ALT_MASK));
        }
        return rulesButton;
    }

    private JMenuItem getPlayerButton() {
        if(playerButton == null) {
            playerButton = new JMenuItem();
            playerButton.setText("Player");
            playerButton.setAction(getPlayerButtonAction());
            playerButton.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_P, ActionEvent.ALT_MASK));
        }
        return playerButton;
    }
    RuleOptionsView tempRules = null;
    @SuppressWarnings("serial")
    private AbstractAction getRulesButtonAction() {
        if(rulesButtonAction == null) {
            rulesButtonAction = new AbstractAction("Rules", null) {
                public void actionPerformed(ActionEvent evt) {
                    if(tempRules == null)
                        tempRules = new RuleOptionsView();
                    else
                        tempRules.setVisible(true);
                }
            };
        }
        return rulesButtonAction;
    }
    @SuppressWarnings("serial")
    private AbstractAction getPlayerButtonAction() {
        if(playerButtonAction == null) {
            playerButtonAction = new AbstractAction("Player", null) {
                public void actionPerformed(ActionEvent evt) {
                    pSettings.repaint();
                    pSettings.setVisible(true);
                }
            };
        }
        return playerButtonAction;
    }

    private JTextField getTxtTime() {
        if(txtTime == null) {
            txtTime = new JTextField();
            txtTime.setText("Default");
            txtTime.setBounds(245, 7, 39, 21);
            txtTime.setEditable(false);
            txtTime.setBackground(Color.white);
            txtTime.setSize(45, 21);
        }
        return txtTime;
    }
    private JLabel getLblDie() {
        if(lblDie == null) {
            lblDie = new JLabel();
            lblDie.setText("Die:");
            lblDie.setBounds(471, 9, 23, 17);
            lblDie.setFont(new java.awt.Font("Tahoma", 0, 11));
        }
        return lblDie;
    }

    private JTextField getTxtDie() {
        if(txtDie == null) {
            txtDie = new JTextField();
            txtDie.setBounds(492, 8, 39, 20);
            txtDie.setText("Default");
            txtDie.setEditable(false);
            txtDie.setBackground(Color.white);
            txtDie.setSize(45, 20);
        }
        return txtDie;
    }

    private JLabel getLblTurn() {
        if (lblTurn == null) {
            lblTurn = new JLabel();
            lblTurn.setText("Turn:");
            lblTurn.setBounds(324, 10, 26, 15);
            lblTurn.setOpaque(true);
            lblTurn.setFont(new java.awt.Font("Tahoma", 0, 10));
            lblTurn.setBackground(new java.awt.Color(255, 255, 255));
        }
        return lblTurn;
    }

    private JTextField getTxtTurn() {
        if (txtTurn == null) {
            txtTurn = new JTextField();
            txtTurn.setBounds(350, 8, 60, 20);
            txtTurn.setMargin(new Insets(0, 0, 0, 0));
            txtTurn.setText("Default");
            txtTurn.setEditable(false);
            txtTurn.setBackground(Color.white);
        }
        return txtTurn;
    }

    private JPanel getPanelP1() {
        if (panelP1 == null) {
            panelP1 = new StartBox(Color.red, "Red", 1);
            panelP1.setBounds(55, 40, 150, 120);
            panelP1.setBorder(BorderFactory.createBevelBorder(BevelBorder.LOWERED));
        }
        return panelP1;
    }

    private JPanel getPanelP2() {
        if (panelP2 == null) {
            panelP2 = new StartBox(Color.blue, "Blue", 2);
            panelP2.setBounds(550, 40, 150, 120);
            panelP2.setBorder(BorderFactory.createBevelBorder(BevelBorder.LOWERED));
        }
        return panelP2;
    }

    private JPanel getPanelP4() {
        if (panelP4 == null) {
            panelP4 = new StartBox(Color.yellow, "Yellow", 4);
            panelP4.setBounds(55, 420, 150, 120);
            panelP4.setBorder(BorderFactory.createBevelBorder(BevelBorder.LOWERED));
        }
        return panelP4;
    }

    private JPanel getPanelP3() {
        if (panelP3 == null) {
            panelP3 = new StartBox(Color.green, "Green", 3);
            panelP3.setBounds(550, 420, 150, 120);
            panelP3.setBorder(BorderFactory.createBevelBorder(BevelBorder.LOWERED));
        }
        return panelP3;
    }

    private JLabel getLblP1() {
        if (lblP1 == null) {
            lblP1 = new JLabel();
            lblP1.setText("Default");
            lblP1.setBounds(80, 20, 60, 15);
        }
        return lblP1;
    }

    private JLabel getLblP2() {
        if (lblP2 == null) {
            lblP2 = new JLabel();
            lblP2.setText("Default");
            lblP2.setBounds(575, 20, 60, 15);
        }
        return lblP2;
    }

    private JLabel getLblP4() {
        if (lblP4 == null) {
            lblP4 = new JLabel();
            lblP4.setText("Default");
            lblP4.setBounds(80, 400, 60, 15);
        }
        return lblP4;
    }

    private JLabel getLblP3() {
        if (lblP3 == null) {
            lblP3 = new JLabel();
            lblP3.setText("Default");
            lblP3.setBounds(575, 400, 60, 15);
        }
        return lblP3;
    }

    //-------------View part 2--------------------------------
    Square[] squares = new Square[40];

    /**
     * We failed to find a good algorithm for this.
     * @see Constants.GUI.GUI.Constants for explanations of the variables
     */
    private void initSquares() {
        int x = Constants.GUI.playingfield_firstSquareX, y = Constants.GUI.playingfield_firstSquareY;

        //Phase one, top left section.
        for(int k = 1; k < 6; k++) {
            Square s = new Square(k);
            this.getContentPane().add(s);
            s.setBounds(x, y, Constants.GUI.squareSide, Constants.GUI.squareSide);
            squares[k] = s;
            x += Constants.GUI.squareSide + Constants.GUI.playingfield_spacingX;
        }
        x -= Constants.GUI.squareSide + Constants.GUI.playingfield_spacingX;
        for (int l = 6; l < 10; l++) {
            y -= Constants.GUI.squareSide + Constants.GUI.playingfield_spacingY;
            Square s = new Square(l);
            this.add(s);
            squares[l] = s;
            s.setBounds(x, y, Constants.GUI.squareSide, Constants.GUI.squareSide);
        }
        for (int i = 10; i < 11; i++) { //To bypass duplicate variable "s" problem.
            x += Constants.GUI.squareSide + Constants.GUI.playingfield_spacingX;
            Square s = new Square(i);
            this.add(s);
            squares[i] = s;
            s.setBounds(x, y, Constants.GUI.squareSide, Constants.GUI.squareSide);
        }

        //Phase two, top right section
        x += Constants.GUI.squareSide + Constants.GUI.playingfield_spacingX;
        for (int k = 11; k < 16; k++) {
            Square s = new Square(k);
            this.getContentPane().add(s);
            s.setBounds(x, y, Constants.GUI.squareSide, Constants.GUI.squareSide);
            squares[k] = s;
            y += Constants.GUI.squareSide + Constants.GUI.playingfield_spacingY;
        }
        x += Constants.GUI.squareSide + Constants.GUI.playingfield_spacingX;
        y -= Constants.GUI.squareSide + Constants.GUI.playingfield_spacingY;
        for (int l = 16; l < 20; l++) {
            Square s = new Square(l);
            this.add(s);
            s.setBounds(x, y, Constants.GUI.squareSide, Constants.GUI.squareSide);
            squares[l] = s;
            x += Constants.GUI.squareSide + Constants.GUI.playingfield_spacingX;
        }
        x -= Constants.GUI.squareSide + Constants.GUI.playingfield_spacingX;
        for (int i = 20; i < 21; i++) {
            y += Constants.GUI.squareSide + Constants.GUI.playingfield_spacingY;
            Square s = new Square(i);
            this.add(s);
            squares[i] = s;
            s.setBounds(x, y, Constants.GUI.squareSide, Constants.GUI.squareSide);
        }

        //Phase three, bottom right section
        y += Constants.GUI.squareSide + Constants.GUI.playingfield_spacingY;
        for (int k = 21; k < 26; k++) {
            Square s = new Square(k);
            this.getContentPane().add(s);
            s.setBounds(x, y, Constants.GUI.squareSide, Constants.GUI.squareSide);
            squares[k] = s;
            x -= Constants.GUI.squareSide + Constants.GUI.playingfield_spacingX;
        }
        x += Constants.GUI.squareSide + Constants.GUI.playingfield_spacingX;
        for (int l = 26; l < 30; l++) {
            y += Constants.GUI.squareSide + Constants.GUI.playingfield_spacingY;
            Square s = new Square(l);
            this.add(s);
            squares[l] = s;
            s.setBounds(x, y, Constants.GUI.squareSide, Constants.GUI.squareSide);
        }
        for (int i = 30; i < 31; i++) {
            x -= Constants.GUI.squareSide + Constants.GUI.playingfield_spacingX;
            Square s = new Square(i);
            this.add(s);
            squares[i] = s;
            s.setBounds(x, y, Constants.GUI.squareSide, Constants.GUI.squareSide);
        }

        //Phase four, bottom left section
        x -= Constants.GUI.squareSide + Constants.GUI.playingfield_spacingX;
        for (int k = 31; k < 36; k++) {
            Square s = new Square(k);
            this.getContentPane().add(s);
            s.setBounds(x, y, Constants.GUI.squareSide, Constants.GUI.squareSide);
            squares[k] = s;
            y -= Constants.GUI.squareSide + Constants.GUI.playingfield_spacingY;
        }
        y += Constants.GUI.squareSide + Constants.GUI.playingfield_spacingY;
        for (int l = 36; l < 40; l++) {
            x -= Constants.GUI.squareSide + Constants.GUI.playingfield_spacingX;
            Square s = new Square(l);
            this.add(s);
            squares[l] = s;
            s.setBounds(x, y, Constants.GUI.squareSide, Constants.GUI.squareSide);
        }
        for (int i = 0; i < 1; i++) {
            y -= Constants.GUI.squareSide + Constants.GUI.playingfield_spacingY;
            Square s = new Square(i);
            this.add(s);
            getContentPane().add(getLabelTime());
            squares[i] = s;
            s.setBounds(x, y, Constants.GUI.squareSide, Constants.GUI.squareSide);
        }

        //Fix the start squares
        squares[1].setColor(Color.red);
        squares[11].setColor(Color.blue);
        squares[21].setColor(Color.green);
        squares[31].setColor(Color.yellow);

        addHomeRows();
    }

    private Square[][] homerows = new Square[4][4];

    private void addHomeRows() {

        int k = 40; //-> 55
        for (int i = 0; i < 4; i++) {
            int next = i * 10;
            int x = squares[next].getX();
            int y = squares[next].getY();
            for (int j = 0; j < 4; j++) {
                x += (i == 0) ? Constants.GUI.squareSide + Constants.GUI.playingfield_spacingX : ((i == 2) ? (-Constants.GUI.squareSide - Constants.GUI.playingfield_spacingX) : 0);
                y += (i == 1) ? (Constants.GUI.squareSide + Constants.GUI.playingfield_spacingY) : ((i == 3) ? (-Constants.GUI.squareSide - Constants.GUI.playingfield_spacingY) : 0);
                Square s = new Square(k);
                this.add(s);
                s.setBounds(x, y, Constants.GUI.squareSide, Constants.GUI.squareSide);
                s.setColor(squares[(i * 10) + 1].getColor());
                homerows[i][j] = s; //indexing
                k++;
            }
        }
    }

    NewGameView ngv = null;
    @SuppressWarnings("serial")
    private AbstractAction getAbstractNewGame() {
        if(abstractNewGame == null) {
            abstractNewGame = new AbstractAction("New Game", null) {
                public void actionPerformed(final ActionEvent evt) {
                    if (ngv == null)
                        ngv = new NewGameView();
                    else
                        ngv.setVisible(true);
                }
            };
        }
        return abstractNewGame;
    }

    private final View v = this;

    @SuppressWarnings("serial")
    private AbstractAction getActionSave() {
        if (actionSave == null) {
            actionSave = new AbstractAction("Save", null) {
                public void actionPerformed(ActionEvent evt) {
                    JFileChooser fc = new JFileChooser();
                    int returnVal = fc.showSaveDialog(v);

                    if (returnVal == JFileChooser.APPROVE_OPTION) {
                        EventBus.publish(Events.SAVE_GAME.toString(), fc.getSelectedFile().getAbsolutePath());
                        System.out.println("You chose to save this file: "
                                            + fc.getSelectedFile().getAbsolutePath());
                    }
                }
            };
        }
        return actionSave;
    }

    @SuppressWarnings("serial")
    private AbstractAction getActionLoad() {
        if (actionLoad == null) {
            actionLoad = new AbstractAction("Load", null) {
                public void actionPerformed(ActionEvent evt) {
                    JFileChooser fc = new JFileChooser();

                    //Perhaps later change to have different file extensions like ".ludo"
                    FileFilter filter1 = new ExtensionFileFilter("file", new String[] {"ludo", "file" });
                    fc.setFileFilter(filter1);

                    int returnVal = fc.showOpenDialog(v);
                    if (returnVal == JFileChooser.APPROVE_OPTION) {
                        EventBus.publish(Events.LOAD_GAME.toString(), fc.getSelectedFile().getAbsolutePath());
                        System.out.println("You chose to load this file: "
                                            + fc.getSelectedFile().getAbsolutePath());
                    }
                }
            };
        }
        return actionLoad;
    }

    private void reset() {
        if (battle != null)
            battle.hide();
        battle = null;

        for (StartBox s: startBoxes)
            s.reset();
        for (Square s:squares)
            this.remove(s);
        for (int i = 0; i < homerows.length; i++) {
            for (int j = 0; j < homerows.length; j++) {
                this.remove(homerows[i][j]);
            }
        }
        squares = new Square[40];
        homerows = new Square[4][4];
        initSquares();
        repaint();
    }


    @EventTopicSubscriber(topic = "ERROR")
    public void onError(final String topic, final String message) {
        JOptionPane.showMessageDialog(this, message);
        repaint();
    }

    @EventTopicSubscriber(topic = "WIN")
    public void onWin(final String topic, String player) {
        int  d = JOptionPane.showConfirmDialog(null, player + " is the winner! do you want to start a new game?");
        if (d == 0) {
            new NewGameView();
        } else {
            exitButton.doClick();
        }

        repaint();
    }

    @EventTopicSubscriber(topic = "DICETHROWUPDATE")
    public void onDiceUpdate(final String topic, final Integer result) {
        txtDie.setText(result.toString());
        btnDie.setDieValue(result);
        btnDie.animate();
    }

    @EventTopicSubscriber(topic = "TIME")
    public void onTime(final String topic, final Integer i) {
        this.txtTime.setText(i.toString());
        repaint();
    }

    Battle battle;
    @EventTopicSubscriber(topic = "BATTLE")
    public void onBattle(final String topic, final String question) {
        battle = new Battle(this);
        battle.waitForAnswer(question);
        //EventBus.publish(Events.BATTLE_ANS.toString(), battle.waitForAnswer(question));
        repaint();
    }

    private void die(final Integer value) {
        Integer dieFace = (Integer) value;
        this.txtDie.setText(dieFace.toString());
        if (value > -1) {
            btnDie.setDieValue(value);
        }
        repaint();
    }

    /**
     * This handler specifies what happens every time the Model publishes something to
     * the EventBus. The plan is that View should extract the new locations of player
     * pieces from this, and simply move them accordingly.
     *
     * More granularity can be added for more specific events.
     */
    @EventTopicSubscriber(topic = "UPDATE")
    public void onUpdate(String topic, IModel model) {

        reset();

        playerButton.setEnabled(true);
        //menuFX.setSelected(control.Settings.isFxEnabled);


        /*
         * Set a black boarder round the player names
         */
        lblP1.setBorder(new LineBorder(new java.awt.Color(0, 0, 0), 1, false));
        lblP2.setBorder(new LineBorder(new java.awt.Color(0, 0, 0), 1, false));
        lblP3.setBorder(new LineBorder(new java.awt.Color(0, 0, 0), 1, false));
        lblP4.setBorder(new LineBorder(new java.awt.Color(0, 0, 0), 1, false));
        Integer current = model.getState().getCurrentPlayer();

        /*
         * Set a red color for the current player in game
         */

        if(current == 0)
            lblP1.setBorder(new LineBorder(Color.red, 1, false));
        if(current == 1)
            lblP2.setBorder(new LineBorder(Color.red, 1, false));
        if(current == 2)
            lblP3.setBorder(new LineBorder(Color.red, 1, false));
        if(current == 3)
            lblP4.setBorder(new LineBorder(Color.red, 1, false));

        /*
         * Just go through the players currently in game, pull out their names, color etc,
         * and update this information in the view.
         */

        //dim1 = player (1-4)
        //dim2 = piece index [4]

        JLabel[] pnames = {lblP1, lblP2, lblP3, lblP4};
        playerNames = pnames;

        die(model.getState().getCurrentDieValue());
        for(int i = 0; i < model.getState().getNumOfPlayers(); i++) {

            if(model.getState().getPlayer(i) != null)
                pnames[i].setText(model.getState().getPlayer(i).getPlayerName());
            else
                pnames[i].setText("");
        }

        txtTurn.setText(model.getState().getPlayer(model.getState().getCurrentPlayer()).toString().split(" ")[0]);

        Integer[][] pieces = model.getPlayingField().getPieceLocations();

        for(int i = 0; i < pieces.length; i++) {
            for(int j = 0; j < pieces.length; j++) {
                //System.out.println("Player "+i+", piece "+j+": "+pieces[i][j]);
                Integer p = pieces[i][j];
                if(p != null) {
                    if(p >= 0 && p < 40) {
                        squares[p].addPiece(new Piece(model.getState().getPlayer(i).getPlayerColor(), p));
                    }
                    if(p > 39 && p < 56)
                        homerows[i][p % 4].addPiece(new Piece(model.getState().getPlayer(i).getPlayerColor(), p));
                    if(p > 55 && p < 60)
                        panelP1.specificAdd(new Piece(model.getState().getPlayer(i).getPlayerColor(), p), p % 56);
                    if(p > 59 && p < 64)
                        panelP2.specificAdd(new Piece(model.getState().getPlayer(i).getPlayerColor(), p), p % 60);
                    if(p > 63 && p < 68)
                        panelP3.specificAdd(new Piece(model.getState().getPlayer(i).getPlayerColor(), p), p % 64);
                    if(p > 67 && p < 72)
                        panelP4.specificAdd(new Piece(model.getState().getPlayer(i).getPlayerColor(), p), p % 68);
                    //System.out.println("Pieces: " +p);
                }
            }
        }
        /*
         * Change the color of the field corresponding to the players color
         */

        if(model.getState().getPlayer(0) != null)
            squares[1].setColor(model.getState().getPlayer(0).getPlayerColor());
        if(model.getState().getPlayer(1) != null)
            squares[11].setColor(model.getState().getPlayer(1).getPlayerColor());
        if(model.getState().getPlayer(2) != null)
            squares[21].setColor(model.getState().getPlayer(2).getPlayerColor());
        if(model.getState().getPlayer(3) != null)
            squares[31].setColor(model.getState().getPlayer(3).getPlayerColor());

        for(int i = 0; i < 4; i++) {
            if(model.getState().getPlayer(i) != null)
                homerows[i][0].setColor(model.getState().getPlayer(i).getPlayerColor());
            if(model.getState().getPlayer(i) != null)
                homerows[i][1].setColor(model.getState().getPlayer(i).getPlayerColor());
            if(model.getState().getPlayer(i) != null)
                homerows[i][2].setColor(model.getState().getPlayer(i).getPlayerColor());
            if(model.getState().getPlayer(i) != null)
                homerows[i][3].setColor(model.getState().getPlayer(i).getPlayerColor());
            if(model.getState().getPlayer(i) != null)
                startBoxes[i].setColor(model.getState().getPlayer(i).getPlayerColor());

            //End of the repaint of the color corresponding to each player.
        }
        repaint();

        System.out.println("View: UPDATE done");
    }
}
